package com.xiaoluo.lottery.service;

import com.xiaoluo.lottery.common.asyn.AsynQueue;
import com.xiaoluo.lottery.common.enums.HBStatusEnums;
import com.xiaoluo.lottery.common.enums.OrderStatusEnums;
import com.xiaoluo.lottery.common.enums.PayBuEnums;
import com.xiaoluo.lottery.common.utils.ApplicationContextUtil;
import com.xiaoluo.lottery.common.utils.FrequencyUtil;
import com.xiaoluo.lottery.common.weixin.base.IPaidCallBack;
import com.xiaoluo.lottery.common.weixin.WeiXinClient;
import com.xiaoluo.lottery.common.weixin.hb.beans.HbInfoReq;
import com.xiaoluo.lottery.common.weixin.hb.beans.HbInfoRes;
import com.xiaoluo.lottery.common.weixin.hb.beans.RedPackReq;
import com.xiaoluo.lottery.common.weixin.hb.beans.RedPackRes;
import com.xiaoluo.lottery.common.weixin.msg.beans.TemplateMsgReq;
import com.xiaoluo.lottery.common.weixin.msg.beans.TemplateMsgRes;
import com.xiaoluo.lottery.common.weixin.pay.beans.*;
import com.xiaoluo.lottery.common.weixin.base.ReturnCode;
import com.xiaoluo.lottery.common.weixin.pay.enums.TradeTypeEnums;
import com.xiaoluo.lottery.common.weixin.utils.Configure;
import com.xiaoluo.lottery.dao.HbInfoJdbcDao;
import com.xiaoluo.lottery.dao.OrderJdbcDao;
import com.xiaoluo.lottery.entity.HbInfo;
import com.xiaoluo.lottery.entity.Order;
import com.xiaoluo.lottery.exception.LotteryException;
import com.xl.tool.util.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Date;

/**
 * Project : lottery
 * Package : com.xiaoluo.lottery.service
 * Created by Willian.
 * Date  : 2016/4/6 18:10.
 */
@Service
@Transactional
public class WeiXinServiceImpl {
    private static final Logger log= LoggerFactory.getLogger(WeiXinServiceImpl.class);

    @Autowired
    private OrderJdbcDao orderJdbcDao;
    @Autowired
    private HbInfoJdbcDao hbInfoJdbcDao;

    /**
     * 统一下单，生成支付preid
     * @param record
     * @return
     */
    public Order doPay(Order record,PayBuEnums bu,IPaidCallBack paidCallBack){

        log.info("doPay order openId:{},totalFee:{},extendId:{}",record.getOpenid(),record.getTotalFee(),record.getExtendId());

        record.setOrderNo(String.valueOf(System.currentTimeMillis()));
        record.setStatus((byte)OrderStatusEnums.INIT.getValue());
        record.setCreateAt(new Date());
        record.setBuType(bu.getValue());
        record.setCbClass(paidCallBack.getClass().getName());
        //insert
        orderJdbcDao.insert(record);
        record = orderJdbcDao.select(record.getOrderNo());

        try {
            UnifiedOrderReq req = new UnifiedOrderReq();
            req.setNotify_url(Configure.getPayNotifyUrl()); /**支付通知url**/
            req.setBody(record.getBody());
            req.setDetail(record.getDetail());
            req.setOut_trade_no(String.valueOf(record.getOrderNo()));
            req.setTotal_fee(record.getTotalFee());
            req.setTrade_type(TradeTypeEnums.JSAPI.getValue());
            req.setOpenid(record.getOpenid());

            if(record.getClientIp() !=null && !"".equals(record.getClientIp()) ){
                req.setSpbill_create_ip(record.getClientIp());
            }else {
                req.setSpbill_create_ip(Configure.getIP());
            }

            req.setAttach(record.getAttach());

            UnifiedOrderRes  res = WeiXinClient.unifiedorder(req);

            if(ReturnCode.SUCCESS.equals(res.getReturnCode())){
                if(ReturnCode.SUCCESS.equals(res.getResultCode())){
                    log.info("doPay request sucess,openId:{},extendId:{},PrepayId:{}",record.getOpenid(),record.getExtendId(),res.getPrepayId());
                    record.setPrepayId(res.getPrepayId());
                    record.setStatus((byte) OrderStatusEnums.PREPAID.getValue());
                }else {
                    log.info("doPay request error,code:{},desc:{}",res.getReturnCode(), res.getReturnMsg());
                    record.setStatus((byte) OrderStatusEnums.PAYERROR.getValue());
                    record.setRemark(String.format("CODE:%S,DESC:%S",res.getReturnCode(),res.getReturnMsg()));
                }
            }else {
                log.info("doPay request error,code:{},desc:{}",res.getReturnCode(), res.getReturnMsg());
                record.setStatus((byte) OrderStatusEnums.PAYERROR.getValue());
                record.setRemark(String.format("CODE:%S,DESC:%S", res.getReturnCode(), res.getReturnMsg()));
            }
            record.setUpdateAt(new Date());
            orderJdbcDao.update(record);

        } catch (Exception e) {
            log.error(e.getMessage(),e);
        }

        return record;
    }

    /**
     * 查询订单
     * @param id
     * @param bu
     * @return
     */
    public OrderQueryRes queryOrder(Long id,PayBuEnums bu){
        log.info("query Order id:{}",id);

        OrderQueryRes res = new OrderQueryRes();
        Order order = orderJdbcDao.selectByType(bu.getValue(), id);

        if(order != null){
            OrderQueryReq req = new OrderQueryReq();
            req.setOut_trade_no(order.getOrderNo());
            try {
                res = WeiXinClient.orderquery(req);
                return res;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return res;
    }

    /**
     * 关闭订单
     * @param
     * @return
     */
    public CloseOrderRes closeOrder(Long id,PayBuEnums bu){
        log.info("close Order id:{}",id);
        CloseOrderRes res = new CloseOrderRes();

        Order order = orderJdbcDao.selectByType(bu.getValue(), id);
        if(order != null){
            CloseOrderReq req = new CloseOrderReq();
            req.setOut_trade_no(order.getOrderNo());
            try {
                res = WeiXinClient.closeorder(req);
                return res;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return res;
    }

    /**
     * 申请退款
     * @param
     * @return
     */
    public RefundRes refund(Long id,PayBuEnums bu){
        log.info("refund id:{}",id);

        RefundRes res = new RefundRes();
        Order order = orderJdbcDao.selectByType(bu.getValue(), id);

        if(order != null){
            RefundReq req = new RefundReq();
            req.setOut_trade_no(order.getOrderNo());
            try {
                res = WeiXinClient.refund(req);
                return  res;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return res;
    }

    /**
     * 查询退款
     * @param
     * @return
     */
    public RefundQueryRes queryRefund(Long id,PayBuEnums bu){
        log.info("queryRefund id:{}",id);

        RefundQueryRes res = new RefundQueryRes();
        Order order = orderJdbcDao.selectByType(bu.getValue(), id);
        if(order != null){
            RefundQueryReq req = new RefundQueryReq();
            req.setOut_trade_no(order.getOrderNo());
            try {
                res = WeiXinClient.refundquery(req);
                return res;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return res;
    }

    /**
     * 发红包
     * @param hb
     * @return
     */
    public RedPackRes sendHb(HbInfo hb){
        return sendHb(hb,false);
    }

    /**
     * 发放红包
     *
     * @param hb
     * @param reSend
     * @return
     */
    private RedPackRes sendHb(HbInfo hb,boolean reSend){
        log.info("sendHb open id:{}",hb.getOpenid());

        RedPackRes res = new RedPackRes();

        if(!reSend){
            hb.setHbCode(String.valueOf(System.currentTimeMillis()));
            hb.setCreateAt(new Date());
            hb.setStatus((byte)HBStatusEnums.SENDING.getValue());
        }

        RedPackReq req = new RedPackReq();
        req.setMch_billno(hb.getHbCode());
        req.setRe_openid(hb.getOpenid());
        req.setWishing(hb.getWishing());
        req.setTotal_amount(hb.getTotalAmount());
        req.setRemark(hb.getRemark());

        if(hb.getTotalNum() ==null || hb.getTotalNum() ==0){
            req.setTotal_num(1);
            hb.setTotalNum(1);
        }else {
            req.setTotal_num(hb.getTotalNum());
        }

        if(hb.getSendName() !=null && !"".equals(hb.getSendName()) ){
            req.setSend_name(hb.getSendName());
        }else {
            req.setSend_name(Configure.SEND_NAME);
            hb.setSendName(Configure.SEND_NAME);
        }

        if(hb.getClientIp() !=null && !"".equals(hb.getClientIp()) ){
            req.setClient_ip(hb.getClientIp());
        }else {
            req.setClient_ip(Configure.getIP());
            hb.setClientIp(Configure.getIP());
        }

        if(hb.getActName() !=null && !"".equals(hb.getActName()) ){
            req.setAct_name(hb.getActName());
        }else {
            req.setAct_name(Configure.ACT_NAME);
            hb.setActName(Configure.ACT_NAME);
        }

        if(hb.getRemark() !=null && !"".equals(hb.getRemark()) ){
            req.setRemark(hb.getRemark());
        }else {
            req.setRemark(Configure.HB_REMARK);
            hb.setRemark(Configure.HB_REMARK);
        }


        try {
            if(!reSend){
                hbInfoJdbcDao.insert(hb);
            }else {
                hbInfoJdbcDao.Update(hb);
            }

            //if (FrequencyUtil.can(hb.getOpenid())){}

            res = WeiXinClient.sendHb(req);

            if(ReturnCode.SUCCESS.equals(res.getReturnCode()) && ReturnCode.SUCCESS.equals(res.getResultCode())){
                log.info("send hb sucessed, sendListId:{}",res.getSendListid());
                hb.setStatus((byte)HBStatusEnums.SENT.getValue());
                hb.setReason(res.getSendListid());
            }else {
                log.info("send hb Fail, code:{},msg:{}",res.getReturnCode(),res.getReturnMsg());
                hb.setStatus((byte)HBStatusEnums.FAILED.getValue());
                hb.setReason(res.getReturnMsg());
            }
            hbInfoJdbcDao.Update(hb);
        } catch (Exception e) {
            log.error(e.getMessage(),e);
        }

        return res;
    }
    /**
     *
     * @param hbcode
     * @return
     */
    public RedPackRes reSendHb(String hbcode){

        HbInfo hbInfo = hbInfoJdbcDao.select(hbcode);
        //
        if(HBStatusEnums.FAILED.getValue() == hbInfo.getStatus()){
            hbInfo.setStatus((byte)HBStatusEnums.RESENDING.getValue());
            hbInfo.setUpdateAt(new Date());
            hbInfo.setReason("");
            return sendHb(hbInfo,true);
        }
        return null;
    }
    /**
     * 获取红包信息
     * @param hbcode
     * @return
     */
    public HbInfo gethbinfo(String  hbcode){
        log.info("gethbinfo hbcode:{}",hbcode);

        HbInfoReq req = new HbInfoReq();
        req.setMch_billno(hbcode);

        try {
            HbInfoRes res = WeiXinClient.gethbinfo(req);
            HbInfo hb =  hbInfoJdbcDao.select(hbcode);

            if(hb != null){
                if(ReturnCode.SUCCESS.equals(res.getReturnCode())
                        && ReturnCode.SUCCESS.equals(res.getResultCode())){
                    hb.setStatus((byte) HBStatusEnums.valueOf(res.getStatus()).getValue());
                    Integer totalAmount = res.getTotalAmount() != null?Integer.parseInt(res.getTotalAmount()):0;
                    Integer refundAmount = res.getRefundAmount() != null?Integer.parseInt(res.getRefundAmount()):0;

                    Date rcvTime = res.getRcvTime() != null ?DateUtil.formateStringToDate(res.getRcvTime(), "yyyy-MM-dd HH:mm:ss"):null;
                    Date refundTime = res.getRefundTime() != null ?DateUtil.formateStringToDate(res.getRefundTime(), "yyyy-MM-dd HH:mm:ss"):null;

                    hb.setTotalAmount(totalAmount);
                    hb.setRefundAmount(refundAmount);
                    hb.setRcvTime(rcvTime);//2015-04-21 20:00:00
                    hb.setRefundTime(refundTime);//2015-04-21 20:00:00
                    hbInfoJdbcDao.Update(hb);
                }else {
                    hb.setStatus((byte)HBStatusEnums.FAILED.getValue());
                    hb.setReason(res.getReturnMsg());
                    hb.setUpdateAt(new Date());
                    hbInfoJdbcDao.errorUpdate(hb);
                }
            }

            return hb;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 发送消息
     * @param msg
     * @return
     */
    public TemplateMsgRes sendMsg(TemplateMsgReq msg){
        log.info("sendMsg tpl:{},toUser:{}",msg.getTemplate_id(),msg.getTouser());

        try {
            return  WeiXinClient.sendMsg(msg);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     *
     * @param request
     * @return
     */
    @Transactional
    public OrderNotifyRes paidCallBack(HttpServletRequest request){
        log.info("paidCallBack");

        OrderNotifyRes res  = new OrderNotifyRes(ReturnCode.FAIL,"");

        String xml = readXml(request);
        log.info("paidCallBack Response:{}",xml);

        if(xml != null && !"".equals(xml)){
            try {
                OrderNotifyReq req = WeiXinClient.orderNotify(xml);

                log.info("paidCallBack ,code:{},desc:{}",req.getReturn_code(), req.getReturn_msg());

                if(ReturnCode.SUCCESS.equals(req.getReturn_code())){

                    log.info("reqest tradeNo:{}",req.getOut_trade_no());
                    Order record = orderJdbcDao.select(req.getOut_trade_no());

                    if(record != null){

                        if(record.getStatus()!=OrderStatusEnums.SUCCESS.getValue()){
                            if(ReturnCode.SUCCESS.equals(req.getResult_code())){
                                log.info("paidCallBack sucessed.");
                                record.setTotalFee(req.getTotal_fee());
                                record.setCashFee(req.getCash_fee());
                                record.setCashFeeType(req.getCash_fee_type());
                                record.setCouponFee(req.getCoupon_fee());
                                record.setCouponCount(req.getCoupon_count());
                                record.setCouponId(req.getCoupon_id_$n());
                                record.setTransactionId(req.getTransaction_id());
                                record.setStatus((byte) OrderStatusEnums.SUCCESS.getValue());
                                record.setPaidDate(new Date());
                            }else {
                                log.info("paidCallBack error,code:{},desc:{}",req.getReturn_code(), req.getReturn_msg());
                                record.setStatus((byte) OrderStatusEnums.PAYERROR.getValue());
                                //record.setRemark(String.format("CODE:%S,DESC:%S",req.getReturn_code(),req.getReturn_msg()));
                            }
                            record.setUpdateAt(new Date());
                            orderJdbcDao.callBackUpdate(record);
                            AsynQueue.push(record);
                        }

                    }else {
                        log.info("oho... could not found record:{}",req.getOut_trade_no());
                    }

                }else {
                    log.info("paidCallBack error,code:{},desc:{}", req.getErr_code(), req.getErr_code_des());
                }

                res.setReturn_code(ReturnCode.SUCCESS);
            } catch (Exception e) {
                log.error(e.getMessage(),e);
            }
        }

        return res;
    }


    /**
     * 读取xml
     * @param request
     * @return
     */
    private String readXml(HttpServletRequest request){

        String line = null;
        StringBuffer buffer = new StringBuffer();

        try {
            BufferedReader in = new BufferedReader(new InputStreamReader(request.getInputStream()));
            while ((line = in.readLine()) != null){
                buffer.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        //
        return buffer.toString();
    }
}
